package algorithms.sedgewick.part1.percolation;
public class Percolation {

	private final int SIZE;
	private final WeightedQuickUnionUF unionUtil;
	private final WeightedQuickUnionUF unionUtilBackwash;
	//Sites 0,N^2-1 correspond to dummy sites
	private final boolean[] openSites;
	private final int NSquare;
	
	public Percolation(int N){
		NSquare = N*N;
		//2 sites for dummy nodes to calculte percolation status
		unionUtil = new WeightedQuickUnionUF(NSquare+2);
		unionUtilBackwash = new WeightedQuickUnionUF(NSquare+1);
		openSites = new boolean[NSquare+2];
		SIZE = N;
		
		openSites[0]=true;
		openSites[NSquare+1] =true;
	}
	private void validateIndices(int i,int j){
		if(i>SIZE || j>SIZE|| i<1 || j<1){
			System.out.println(i+","+j);
			throw new java.lang.IndexOutOfBoundsException();
		}
	}
	public void open(int i, int j)    {
		validateIndices(i,j);
		i--;
		int idx = getSingleArrayIndex(i, j);
		//North connection
		if(i==0){
			connectIfOpen(idx, 0);
		}
		else{
			connectIfOpen(idx, idx-SIZE);
		}
		//South Connection
		if(i==(SIZE-1)){
			connectIfOpenWithoutBackwash(idx, NSquare+1);
		}
		else{
			connectIfOpen(idx, idx+SIZE);
		}
		//WEST Connection
		if(j!=1){
			connectIfOpen(idx, idx-1);
		}
		//East Connection
		if(j!=SIZE){
			connectIfOpen(idx, idx+1);
		}
		openSites[idx]=true;
	}
	private void connectIfOpenWithoutBackwash(int siteToConnect,int connectToSite){
		if(openSites[connectToSite]){
			unionUtil.union(connectToSite, siteToConnect);
		}
	}
	private void connectIfOpen(int siteToConnect,int connectToSite){
		if(openSites[connectToSite]){
			unionUtil.union(connectToSite, siteToConnect);
			unionUtilBackwash.union(connectToSite, siteToConnect);
		}
	}
	/**
	 * 
	 * @param i 0 based x index <= N-1
	 * @param j 0 based y index <= N-1
	 * @return
	 */
	private int getSingleArrayIndex(int i, int j){
		return SIZE*i+j;
	}
	public boolean isOpen(int i, int j) {
		validateIndices(i,j);
		i--;
		return openSites[getSingleArrayIndex(i, j)];
	}
	public boolean isFull(int i, int j)   {
		validateIndices(i,j);
		i--;
		return unionUtilBackwash.connected(0, getSingleArrayIndex(i, j));
	}
	public boolean percolates(){
		return unionUtil.connected(0, NSquare+1);
	}
}